﻿using Castle.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;

namespace Utility
{
    public static class ServiceCollectionUntily
    {
        /// <summary>
        /// 自定义获取键功能(默认关闭)
        /// </summary>
        private static bool enableServiceKey = false;
        private static ProxyGenerator generator = new ProxyGenerator();
        private static ICollection<Assembly> assemblies = new Collection<Assembly>();
        private static ServiceCollection serviceDescriptors;
        private static IServiceProvider serviceProvider;
        /// <summary>
        /// 初始化服务
        /// </summary>
        /// <param name="assemblies">需要加载的程序集</param>
        public static void Init(params Assembly[] assemblies)
        {
            serviceDescriptors = new ServiceCollection();
            RegisterServices(assemblies);
            ResolveServices();
            BuildScopeServiceProvider();
        }
        /// <summary>
        /// 注册程序集
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        private static void RegisterServices(Assembly[] assblies)
        {
            //先加载外部程序集
            foreach (var item in assblies)
            {
                assemblies.Add(item);
            }
            //加载自己
            assemblies.Add(RegisterEntry.RegisterImport());
        }
        /// <summary>
        /// 解析服务
        /// </summary>
        public static void ResolveServices()
        {
            foreach (var assembly in assemblies)
            {
                foreach (var item in assembly.GetTypes())
                {
                    if (item.IsClass && item.CustomAttributes.Any(x =>
                        x.AttributeType == typeof(InjectSingletonAttribute) ||
                        x.AttributeType == typeof(InjectScopedAttribute) ||
                        x.AttributeType == typeof(InjectTransientAttribute)
                        ))
                    {
                        var interfaceInfos = item.GetInterfaces();
                        var className = item.Name;

                        //注入类
                        if (interfaceInfos.Count() <= 0)
                        {
                            switch (item.CustomAttributes.First().AttributeType.Name)
                            {
                                case nameof(InjectSingletonAttribute):
                                    serviceDescriptors.AddSingleton(item);
                                    break;
                                case nameof(InjectScopedAttribute):
                                    serviceDescriptors.AddScoped(item);
                                    break;
                                case nameof(InjectTransientAttribute):
                                    serviceDescriptors.AddTransient(item);
                                    break;
                                default:
                                    throw new ArgumentOutOfRangeException("容器初始化服务异常");
                            }
                            continue;
                        }

                        //注入接口和类，包含多个类继承自同一个接口的情况
                        foreach (var interfaceInfo in interfaceInfos)
                        {
                            switch (item.CustomAttributes.First().AttributeType.Name)
                            {
                                case nameof(InjectSingletonAttribute):
                                    if (enableServiceKey)
                                    {
                                        serviceDescriptors.AddKeyedSingleton(interfaceInfo, className, item);
                                    }
                                    serviceDescriptors.AddSingleton(interfaceInfo, item);
                                    break;
                                case nameof(InjectScopedAttribute):
                                    if (enableServiceKey)
                                    {
                                        serviceDescriptors.AddKeyedScoped(interfaceInfo, className, item);
                                    }
                                    serviceDescriptors.AddScoped(interfaceInfo, item);
                                    break;
                                case nameof(InjectTransientAttribute):
                                    if (enableServiceKey)
                                    {
                                        serviceDescriptors.AddKeyedTransient(interfaceInfo, className, item);
                                    }
                                    serviceDescriptors.AddTransient(interfaceInfo, item);
                                    break;
                                default:
                                    throw new ArgumentOutOfRangeException("容器初始化服务异常");
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 构造域服务容器对象
        /// </summary>
        private static void BuildScopeServiceProvider()
        {
            serviceProvider = serviceDescriptors.BuildServiceProvider().CreateScope().ServiceProvider;
        }
        /// <summary>
        /// 根据自定义key来获取服务
        /// </summary>
        /// <typeparam name="T">服务类型</typeparam>
        /// <param name="key">key</param>
        /// <returns></returns>
        public static T GetRequiredKeyedService<T>(object key)
        {
            return serviceProvider.GetRequiredKeyedService<T>(key);
        }
        /// <summary>
        /// 根据类型来获取应用的服务
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T GetRequiredService<T>()
        {
            return serviceProvider.GetRequiredService<T>();
        }
        /// <summary>
        /// 使用类代理(必须要虚方法,否则不生效,因为虚方法能通过重写来实现拦截)
        /// </summary>
        /// <typeparam name="T">代理类型</typeparam>
        /// <param name="target">代理对象</param>
        /// <returns></returns>
        public static T PorxyWithVirtualClass<T>(this T target, InterceptAOP? intercept = default) where T:class
        {
            if(intercept == null)
            {
                intercept = new InterceptAOP();
            }
            return generator.CreateClassProxyWithTarget(target, intercept);
        }
        /// <summary>
        /// 使用接口代理
        /// </summary>
        /// <typeparam name="T">代理类型</typeparam>
        /// <param name="target">代理对象</param>
        /// <returns></returns>
        public static T PorxyWithInterface<T>(this T target, InterceptAOP? intercept = default) where T : class
        {
            if (intercept == null)
            {
                intercept = new InterceptAOP();
            }
            return generator.CreateInterfaceProxyWithTargetInterface(target, intercept);
        }
        /// <summary>
        /// 开启自定义键获取服务功能(需要在Init方法前执行否则不生效)
        /// </summary>
        public static void EnableServiceKey()
        {
            enableServiceKey = true;
        }
    }
}
